﻿using System;
using System.IO;
using log4net;

namespace Browis.Package
{
    public enum SectionOption : uint
    {
    }

    /// <summary>
    /// 包的节信息流
    /// </summary>
    public class SectionStream : Stream
    {
        private const int SectionHeaderSize = 88;
        private const int SectionNameSize = 64;

        private ILog log = null;
        private Stream innerStream;
        private FileAccess mode;
        private long length;
        private long innerStreamOffset;

        public String Name
        {
            get;
            private set;
        }

        public Int64 Offset
        {
            get;
            private set;
        }

        public override long Length
        {
            get
            {
                return length;
            }
        }

        public UInt32 Checksum
        {
            get;
            private set;
        }

        public UInt32 Options
        {
            get;
            private set;
        }

        protected SectionStream()
        {
        }

        private void ReadSectionHeader()
        {
            Byte[] buffer = new Byte[SectionHeaderSize];

            if (innerStream.Read(buffer, 0, buffer.Length) != buffer.Length)
            {
                log.Warn("流长度不足，无法读取节头信息！");
                throw new Exception("innerStream");
            }
            Name = System.Text.Encoding.GetEncoding("gb2312").GetString(buffer, 0, SectionNameSize).Trim((Char)0);
            Offset = BitConverter.ToInt64(buffer, SectionNameSize);
            length = BitConverter.ToInt64(buffer, SectionNameSize + sizeof(Int64));
            Checksum = BitConverter.ToUInt32(buffer, SectionNameSize + sizeof(Int64) * 2);
            Options = BitConverter.ToUInt32(buffer, SectionNameSize + sizeof(Int32) + sizeof(Int64) * 2);
        }

        /// <summary>
        /// 写节头信息
        /// </summary>
        private void WriteSectionHeader()
        {
            Byte[] buffer = new Byte[SectionHeaderSize];

            Byte[] temp = System.Text.Encoding.GetEncoding("gb2312").GetBytes(Name);
            Array.Copy(temp, buffer, temp.Length > SectionNameSize ? SectionNameSize : temp.Length);

            Array.Copy(BitConverter.GetBytes(Offset), 0, buffer, SectionNameSize, sizeof(Int64));
            Array.Copy(BitConverter.GetBytes(Length), 0, buffer, SectionNameSize + sizeof(Int64), sizeof(Int64));
            Array.Copy(BitConverter.GetBytes(Checksum), 0, buffer, SectionNameSize + sizeof(Int64) * 2, sizeof(UInt32));
            Array.Copy(BitConverter.GetBytes(Options), 0, buffer, SectionNameSize + sizeof(Int64) * 2 + sizeof(UInt32), sizeof(UInt32));

            innerStream.Position = innerStreamOffset;
            innerStream.Write(buffer, 0, buffer.Length);
            innerStream.Position = innerStreamOffset + Length + SectionHeaderSize;
        }

        public static SectionStream Open(Stream innerStream)
        {
            if (innerStream == null)
                throw new ArgumentNullException("innerStream");

            if (innerStream.CanRead == false)
                throw new ArgumentException("innerStream Cann't read");

            SectionStream ss = new SectionStream();

            ss.mode = FileAccess.Read;
            ss.innerStream = innerStream;
            ss.innerStreamOffset = innerStream.Position;

            ss.ReadSectionHeader();

            return ss;
        }

        public static SectionStream Create(Stream innerStream, String name, UInt32 options, ILog myLog)
        {
            if (innerStream == null)
            {
                myLog.Warn("数据包流为空！");
                throw new ArgumentNullException("innerStream");
            }
            if (innerStream.CanWrite == false)
            {
                myLog.Warn("数据包流不可写！");
                throw new ArgumentException("innerStream Cann't write");
            }
            SectionStream ss = new SectionStream();

            ss.log = myLog;
            ss.mode = FileAccess.ReadWrite;
            ss.innerStream = innerStream;
            ss.innerStreamOffset = innerStream.Position;

            ss.Name = name;
            ss.WriteSectionHeader();

            return ss;
        }

        public static SectionStream Lookup(Stream innerStream, String name, ILog myLog)
        {
            if (innerStream == null)
            {
                myLog.Warn("数据包流为空！");
                throw new ArgumentNullException("innerStream");
            }
            if (name == null)
            {
                myLog.Warn("待查找的节名称为空！");
                throw new ArgumentNullException("name");
            }
            SectionStream ss = new SectionStream();

            ss.log = myLog;
            ss.mode = FileAccess.Read;
            ss.innerStream = innerStream;
            ss.innerStreamOffset = innerStream.Position;

            while (innerStream.Position < innerStream.Length)
            {
                ss.ReadSectionHeader();
                if (ss.Name.Equals(name, StringComparison.CurrentCultureIgnoreCase) == true)
                    return ss;

                ss.innerStreamOffset = ss.innerStreamOffset + ss.Length + SectionHeaderSize;
                innerStream.Position = ss.innerStreamOffset;
            }

            return null;
        }

        public override bool CanRead
        {
            get
            {
                return (mode == FileAccess.Read) || (mode == FileAccess.ReadWrite);
            }
        }

        public override bool CanSeek
        {
            get
            {
                return false;
            }
        }

        public override bool CanWrite
        {
            get
            {
                return (mode == FileAccess.Write) || (mode == FileAccess.ReadWrite);
            }
        }

        public override void Flush()
        {
            if (innerStream != null)
            {
                innerStream.Flush();
            }
        }

        //有BUG，如果未通过SectionStream，而是直接操作innerStream将导致Position返回错误的值
        public override long Position
        {
            get
            {
                return innerStream.Position - innerStreamOffset - SectionHeaderSize;
            }
            set
            {
                if (value < 0)
                    throw new ArgumentOutOfRangeException("Position");
                else if (value > Length)
                    throw new ArgumentOutOfRangeException("Position");

                innerStream.Position = innerStreamOffset + SectionHeaderSize + value;
            }
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            long pos = Position;

            if (origin == SeekOrigin.Begin)
                pos = innerStreamOffset + SectionHeaderSize + offset;
            else if (origin == SeekOrigin.Current)
                pos = pos + offset;
            else
                pos = innerStreamOffset + SectionHeaderSize + Length + offset;

            if (pos < innerStreamOffset + SectionHeaderSize || pos > innerStreamOffset + SectionHeaderSize + Length)
                throw new ArgumentOutOfRangeException("offset");

            return innerStream.Seek(pos, SeekOrigin.Begin);
        }

        public override void SetLength(long value)
        {
            throw new NotSupportedException("SetLength");
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            if (CanRead == false)
            {
                log.Warn("文件流已关闭");
                throw new ObjectDisposedException("文件流已关闭");
            }
            if (buffer == null)
            {
                log.Warn("缓冲区为null");
                throw new ArgumentNullException("buffer");
            }

            if (offset < 0)
            {
                log.Warn("偏移量小于0");
                throw new ArgumentOutOfRangeException("offset");
            }
            if (count < 0)
            {
                log.Warn("指定的读取个数小于0");
                throw new ArgumentOutOfRangeException("count");
            }
            if (buffer.Length < offset + count)
            {
                log.Warn("缓冲区长度过小");
                throw new ArgumentOutOfRangeException("缓冲区长度过小");
            }

            //读取数据超过节长度，只允许读到节末尾
            if (Position + count > Length)
                count = (int)(Length - Position);

            if (count == 0)
                return 0;

            return innerStream.Read(buffer, offset, count);
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            if (CanWrite == false)
            {
                log.Warn("文件流已关闭");
                throw new ObjectDisposedException("文件流已关闭");
            }

            if (buffer == null)
            {
                log.Warn("待写入缓冲区为空");
                throw new ArgumentNullException("buffer");
            }
            if (offset < 0)
            {
                log.Warn("偏移量小于0");
                throw new ArgumentOutOfRangeException("offset");
            }
            if (count < 0)
            {
                log.Warn("待写入数据量小于0");
                throw new ArgumentOutOfRangeException("count");
            }
            if (buffer.Length < offset + count)
            {
                log.Warn("缓冲区长度过小");
                throw new ArgumentOutOfRangeException("缓冲区长度过小");
            }

            if (count == 0)
                return;

            innerStream.Write(buffer, offset, count);
            //根据写操作后节的数据大小（不含节头），更新数据长度属性
            if (innerStream.Position - innerStreamOffset - SectionHeaderSize > Length)
                length = innerStream.Position - innerStreamOffset - SectionHeaderSize;
        }

        /// <summary>
        /// 释放节信息流，为写操作时，再写一次头信息
        /// </summary>
        /// <param name="disposing">是否释放托管资源</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && innerStream != null)
            {
                if (CanWrite == true)
                    WriteSectionHeader();
                innerStream = null;
            }
        }
    }
}
